package com.anxin.registerManagement.config.shiro;

import com.anxin.registerManagement.config.realm.JwtRealm;
import com.anxin.registerManagement.config.realm.UserNamePassWordRealm;
import com.anxin.registerManagement.filter.JwtFilter;
import org.apache.shiro.authc.Authenticator;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 叶前呈
 * @date 2022/1/23 15:03
 */

@Configuration
public class ShiroConfig {

    /**
     * 注入自定义realm
     *
     * @return
     */
    @Bean
    public UserNamePassWordRealm userNamePassWordRealm() {
        return new UserNamePassWordRealm();
    }

    @Bean
    public JwtRealm jwtRealm() {
        JwtRealm realm = new JwtRealm();
        realm.setCachingEnabled(false);
        return realm;
    }

    /**
     * 初始化Authenticator验证管理器，如不注入，则会导致验证失败返回未登录
     * Authorizer授权器：赋予主体有哪些权限
     */
    @Bean
    public Authenticator authenticator() {
        //扩展父类原方法，捕获原始异常
        MultiRealmAuthenticator authenticator = new MultiRealmAuthenticator();

        /**
         FirstSuccessfulStrategy：只要有一个 Realm 验证成功即可，只返回第一个 Realm 身份验证成功的认证信息，其他的忽略；
         AtLeastOneSuccessfulStrategy：只要有一个 Realm 验证成功即可，和 FirstSuccessfulStrategy 不同，返回所有 Realm 身份验证成功的认证信息；（默认）
         AllSuccessfulStrategy：所有 Realm 验证成功才算成功，且返回所有 Realm 身份验证成功的认证信息，如果有一个失败就失败了。
         */
        //设置多个realm认证策略，一个成功即跳过其它的
        authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
        return authenticator;
    }

    /**
     * 安全管理器
     *
     * @return
     */
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置验证器
        securityManager.setAuthenticator(authenticator());
        // 关联自定义realm
        securityManager.setRealms(
                Arrays.asList(
                        jwtRealm(),
                        userNamePassWordRealm()
                ));
        return securityManager;
    }


    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        // 添加shiro的内置过滤器
        /*
            anon：无需认证就可以访问
            authc：必须认证了才能访问
            user：必须拥有 记住我 功能才能用
            perms：拥有对某讴歌资源的权限才能访问
            role：拥有某个角色权限爱能访问
         */
        // 默认需要认证
        // 注意：若第一次进入网站，访问除login页面以外，shiro会认为没有登录，自动跳转到login.jsp页面，只有通过login之后再访问其他接口才有效
        // FIXME 这里的路径是Controller的路径，而不是访问路径，即是不拦截/user/login => register-management/v1/user/login
        // TODO:下面开启权限认证
//        chainDefinition.addPathDefinition("/user/login", "anon");
//        chainDefinition.addPathDefinition("/**", "jwt");

        return chainDefinition;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager());
        shiroFilterFactoryBean.setLoginUrl("/register-management/v1/login");
        Map<String, Filter> filterMap = new HashMap<>();
        filterMap.put("jwt", new JwtFilter());
        // 配置过滤链
        shiroFilterFactoryBean.setFilters(filterMap);
        shiroFilterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap());

        return shiroFilterFactoryBean;
    }

    /**
     * 因为使用jwt登录，无需session，所以要取消session，减少不必要的开销，【但是会导致druid页面无法登录】
     * 参考文章：https://developpaper.com/spring-boot-plus-integrates-spring-boot-shiro-jwt-permission-management/
     *
     * @return
     */
    @Bean
    public DefaultSessionManager sessionManager() {
        DefaultSessionManager manager = new DefaultSessionManager();
        manager.setSessionValidationSchedulerEnabled(false);
        return manager;
    }

}
